using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using Microsoft.Win32;

namespace EForms
{
	/// <summary>
	/// Allows saving and restoring position and size of any Control.
	/// For simplicity it does not save Form position when minimized.
	/// For ListView it saves and restores widths of colums.
	/// dotnet@bk.ru
	/// </summary>
	[ProvideProperty("RestoreLocation", typeof(Control))]
	[ProvideProperty("RestoreColumnsWidth", typeof(ListView))]
	[ToolboxItem(true)]
	[ToolboxBitmap(typeof(RealPosition))]
	public class RealPosition : Component, IExtenderProvider
	{
		private System.ComponentModel.Container components = null;
		private static string m_RegPath;
		private string m_subRegPath;
		private Hashtable m_properties;
		private bool m_bindedToForm;
		private Form m_parent;

		#region RealPosition Constructors
		public RealPosition()
		{
			m_properties = new Hashtable();

			m_RegPath = @"Software\RestoreState\RealPosition"; // default
			m_bindedToForm = false;
		}
		
		public RealPosition(IContainer parent) : this()
		{
			parent.Add(this);
			InitializeComponent();
		}
		#endregion

		#region Indexer
		// indexer is implemented to simplify implementation of properties
		public Properties this[object o]
		{
			get
			{
				return EnsureExists(o);
			}
			set
			{
				Properties p = EnsureExists(o);
				p = value;
			}
		}
		private Properties EnsureExists(object key)
		{
			Properties p = (Properties)m_properties[key];
			if(p == null)
			{
				p = new Properties(key);
				m_properties[key] = p;
			}
			return p;
		}
		#endregion

		#region Properties
		[
		Category("RealPosition"),
		DefaultValue(@"Software\RestoreState\RealPosition"),
		Description(@"Registry path to store controls' positions")
		]
		public string RegistryPath
		{
			get {return m_RegPath;}
			set {m_RegPath = value;}
		}
		#endregion

		#region Extended Properties

		[Description("Save and restore Control's position and size")]
		[Category("RealPosition")]
		public bool GetRestoreLocation(Control c)
		{
			return this[c].RestoreLocation;
		}
		public void SetRestoreLocation(Control c, bool val)
		{
			this[c].RestoreLocation = val;

			// if c is Form then bind to its Load and Closing events
			// assume that one RealPosition component can be binded to only 1 Form
			if(!m_bindedToForm)
			{
				Form f = c as Form;
				if(f != null)
				{
					f.Closing += new System.ComponentModel.CancelEventHandler(OnClosing);
					f.Load += new System.EventHandler(OnLoad);
					m_subRegPath = @"\" + f.Name + @"\";
					m_parent = f;
					m_bindedToForm = true;
				}
			}
		}
		private bool ShouldSerializeRestoreLocation(Control c)
		{
			if(c is Form) return true;
			if(this[c].RestoreLocation == true) return true;
			else return false;
		}

		private void ResetRestoreLocation(Control c)
		{
			this[c].RestoreLocation = (c is Form);
		}

		[Description("Save and restore ListView's column widths")]
		[Category("RealPosition")]
		public bool GetRestoreColumnsWidth(Control c)
		{
			return this[c].RestoreColumnsWidth;
		}
		public void SetRestoreColumnsWidth(Control c, bool val)
		{
			this[c].RestoreColumnsWidth = val;
		}
		private bool ShouldSerializeRestoreColumnsWidth(Control c)
		{
			return this[c].RestoreColumnsWidth;
		}

		#endregion

		#region Event Handler
		private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			foreach(DictionaryEntry property in m_properties)
			{
				Properties p = (Properties)property.Value;
				string regpath = m_RegPath + m_subRegPath + p.m_subRegPath;
				RegistryKey key = Registry.CurrentUser.CreateSubKey(regpath);
				if(p.RestoreLocation == true) // Save only if RestoreLocation is True
				{
					if(p.m_parent is Form)
					{
						FormWindowState windowState = ((Form)p.m_parent).WindowState;
						key.SetValue("WindowState", (int)windowState);
					}
					SaveControlLocation(key,p);
				}
				ListView lv = p.m_parent as ListView;
				string regpath_headers = regpath + @"\ColumnHeaders";
				if(p.RestoreColumnsWidth == true && lv != null)
				{
					// save ColumnHeaders
					RegistryKey key_headers = Registry.CurrentUser.CreateSubKey(regpath_headers);
					foreach(ColumnHeader ch in lv.Columns)
					{
						key_headers.SetValue(lv.Columns.IndexOf(ch).ToString(),ch.Width);
					}
				}
			}
		}

		private void SaveControlLocation(RegistryKey key, Properties p)
		{
			if(p.m_wasMoved) // save Position
			{
				key.SetValue("Left",p.m_normalLeft);
				key.SetValue("Top", p.m_normalTop);
			}
			if(p.m_wasResized) // save Size
			{
				key.SetValue("Width", p.m_normalWidth);
				key.SetValue("Height", p.m_normalHeight);
			}
		}

		private void OnLoad(object sender, System.EventArgs e)
		{
			m_parent.SuspendLayout();
			foreach(DictionaryEntry property in m_properties)
			{
				Properties p = (Properties)property.Value;
				string regpath = m_RegPath + m_subRegPath + p.m_subRegPath;
				if(p.RestoreLocation == true) // Restore only if RestoreLocation is True
				{
					RegistryKey key = Registry.CurrentUser.OpenSubKey(regpath);

					if(key != null)
					{
						LoadControlLocationAndState(key,p);
					}
				}
				if(p.RestoreColumnsWidth == true)
				{
					string regpath_headers = regpath + @"\ColumnHeaders";
					RegistryKey key_headers = Registry.CurrentUser.OpenSubKey(regpath_headers);
					LoadListViewColumns(key_headers, p);
				}

			}
			m_parent.ResumeLayout();
		}
		private void LoadListViewColumns(RegistryKey key, Properties p)
		{
			ListView lv = p.m_parent as ListView;
			if(lv != null && key != null)
			{
				foreach(ColumnHeader ch in lv.Columns)
				{
					object width = key.GetValue(lv.Columns.IndexOf(ch).ToString());
					if(width != null) ch.Width = (int)width;
				}
			}
		}

		private void LoadControlLocationAndState(RegistryKey key, Properties p)
		{
			object left = key.GetValue("Left");
			object top = key.GetValue("Top");
			object width = key.GetValue("Width");
			object height = key.GetValue("Height");
			object windowState = key.GetValue("WindowState");

			if(left != null && top != null) // set Location
			{
				p.m_parent.Location = new Point((int)left,(int)top);
			}

			if(width != null && height != null) // set Size
			{
				p.m_parent.Size = new Size((int)width,(int)height);
			}

			// set window state for the main form
			Form f = p.m_parent as Form;
			if(f != null && windowState != null)
			{
				f.WindowState = (FormWindowState)windowState;
				// do not allow minimized restore
				if(f.WindowState == FormWindowState.Minimized)
					f.WindowState = FormWindowState.Normal;
			}

		}
		#endregion

		bool IExtenderProvider.CanExtend(object o)
		{
			if(o is Control)
				return true;
			else
				return false;
		}


		/// <summary>
		/// Main purpose: represent Control's location 
		/// Additional purpose: store each Control's "extended" properties 
		/// </summary>
		public class Properties
		{
			public bool RestoreLocation;	// "Extended" property
			public bool RestoreColumnsWidth;// "Extended" property

			// variables to save to Registry
			public int m_normalLeft;
			public int m_normalTop;
			public int m_normalWidth;
			public int m_normalHeight;

			// helper variables
			public bool m_wasResized = false;
			public bool m_wasMoved = false;
			private bool m_isForm;

			public Control m_parent;

			public string m_subRegPath;

			public Properties(object o)
			{
				RestoreLocation = (o is Form); // default true for Form
				RestoreColumnsWidth = false;	// default is false
				m_parent = (Control)o;		// type cast should not result to null
				m_isForm = (o is Form);
				// if o is Form then m_subRegPath is empty string
				m_subRegPath = (o is Form) ? String.Empty : m_parent.Name;
				
				// attach to Resize and Move
				m_parent.Resize += new System.EventHandler(OnResize);
				m_parent.Move += new System.EventHandler(OnMove);
			}
			#region Event Handlers
			private void OnResize(object sender, System.EventArgs e)
			{
				if(!m_isForm || ((Form)m_parent).WindowState == FormWindowState.Normal)
				{
					// save width and height 
					m_wasResized = true;
					m_normalWidth = m_parent.Width;
					m_normalHeight = m_parent.Height;
				}
			}

			private void OnMove(object sender, System.EventArgs e)
			{
				if(!m_isForm || ((Form)m_parent).WindowState == FormWindowState.Normal)
				{
					// save position
					m_wasMoved = true;
					m_normalLeft = m_parent.Left;
					m_normalTop = m_parent.Top;
				}
			}

		#endregion
		}
	
		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}
